package service

import (
	"context"
	"fmt"
	"time"

	"github.com/devops-smartbot/devops-smartbot/services/integration-service/internal/clients"
	"github.com/devops-smartbot/devops-smartbot/services/integration-service/internal/models"
	"github.com/devops-smartbot/devops-smartbot/services/integration-service/internal/repository"
	ticketv1 "github.com/devops-smartbot/devops-smartbot/proto/gen/go/ticket/v1"
	"go.uber.org/zap"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

type IntegrationService struct {
	repo          *repository.IntegrationRepository
	logger        *zap.Logger
	ticketClient  ticketv1.TicketServiceClient
}

func NewIntegrationService(repo *repository.IntegrationRepository, logger *zap.Logger, ticketServiceAddr string) (*IntegrationService, error) {
	// Connect to Ticket Service
	conn, err := grpc.NewClient(ticketServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		return nil, fmt.Errorf("failed to connect to ticket service: %w", err)
	}

	ticketClient := ticketv1.NewTicketServiceClient(conn)

	return &IntegrationService{
		repo:         repo,
		logger:       logger,
		ticketClient: ticketClient,
	}, nil
}

// Jira Methods
func (s *IntegrationService) SyncTicketToJira(ctx context.Context, ticketID, projectKey, issueType string) (*models.JiraIssue, error) {
	// Check if already synced
	existing, err := s.repo.GetTicketJiraMapping(ctx, ticketID)
	if err != nil {
		return nil, err
	}
	if existing != nil {
		return nil, fmt.Errorf("ticket already synced to Jira: %s", existing.JiraKey)
	}

	// Get Jira config
	config, err := s.repo.GetJiraConfig(ctx)
	if err != nil {
		return nil, err
	}
	if config == nil {
		return nil, fmt.Errorf("jira not configured")
	}

	// Get ticket details
	ticketResp, err := s.ticketClient.GetTicket(ctx, &ticketv1.GetTicketRequest{
		Id: ticketID,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to get ticket: %w", err)
	}
	ticket := ticketResp

	// Create Jira client
	jiraClient := clients.NewJiraClient(config, s.logger)

	// Create Jira issue
	jiraIssue, err := jiraClient.CreateIssue(
		ticket.Title,
		ticket.Description,
		issueType,
		ticket.Priority,
	)
	if err != nil {
		return nil, fmt.Errorf("failed to create jira issue: %w", err)
	}

	// Save mapping
	mapping := &models.TicketJiraMapping{
		TicketID:  ticketID,
		JiraKey:   jiraIssue.Key,
		JiraID:    jiraIssue.ID,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	if err := s.repo.CreateTicketJiraMapping(ctx, mapping); err != nil {
		s.logger.Warn("Failed to save ticket-jira mapping", zap.Error(err))
	}

	s.logger.Info("Synced ticket to Jira",
		zap.String("ticket_id", ticketID),
		zap.String("jira_key", jiraIssue.Key))

	return jiraIssue, nil
}

func (s *IntegrationService) SyncJiraIssue(ctx context.Context, jiraKey string) (string, error) {
	// Check if already synced
	existing, err := s.repo.GetJiraTicketMapping(ctx, jiraKey)
	if err != nil {
		return "", err
	}
	if existing != nil {
		return existing.TicketID, nil
	}

	// Get Jira config
	config, err := s.repo.GetJiraConfig(ctx)
	if err != nil {
		return "", err
	}
	if config == nil {
		return "", fmt.Errorf("jira not configured")
	}

	// Create Jira client
	jiraClient := clients.NewJiraClient(config, s.logger)

	// Get Jira issue
	jiraIssue, err := jiraClient.GetIssue(jiraKey)
	if err != nil {
		return "", fmt.Errorf("failed to get jira issue: %w", err)
	}

	// Create ticket
	priority := s.mapPriorityFromJira(jiraIssue.Priority)

	createResp, err := s.ticketClient.CreateTicket(ctx, &ticketv1.CreateTicketRequest{
		Title:       jiraIssue.Summary,
		Description: jiraIssue.Description,
		Priority:    priority,
	})
	if err != nil {
		return "", fmt.Errorf("failed to create ticket: %w", err)
	}

	ticketID := createResp.Ticket.Id

	// Save mapping
	mapping := &models.TicketJiraMapping{
		TicketID:  ticketID,
		JiraKey:   jiraIssue.Key,
		JiraID:    jiraIssue.ID,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}
	if err := s.repo.CreateTicketJiraMapping(ctx, mapping); err != nil {
		s.logger.Warn("Failed to save jira-ticket mapping", zap.Error(err))
	}

	s.logger.Info("Synced Jira issue to ticket",
		zap.String("jira_key", jiraKey),
		zap.String("ticket_id", ticketID))

	return ticketID, nil
}

func (s *IntegrationService) GetJiraConfig(ctx context.Context) (*models.JiraConfig, error) {
	return s.repo.GetJiraConfig(ctx)
}

func (s *IntegrationService) UpdateJiraConfig(ctx context.Context, config *models.JiraConfig) error {
	config.UpdatedAt = time.Now()
	if config.CreatedAt.IsZero() {
		config.CreatedAt = time.Now()
	}

	// Test connection
	jiraClient := clients.NewJiraClient(config, s.logger)
	if err := jiraClient.TestConnection(); err != nil {
		return fmt.Errorf("jira connection test failed: %w", err)
	}

	return s.repo.UpsertJiraConfig(ctx, config)
}

// PagerDuty Methods
func (s *IntegrationService) CreatePagerDutyIncident(ctx context.Context, title, description, urgency, alertID string) (*models.PagerDutyIncident, error) {
	// Check if already created
	if alertID != "" {
		existing, err := s.repo.GetAlertPagerDutyMapping(ctx, alertID)
		if err != nil {
			return nil, err
		}
		if existing != nil {
			return nil, fmt.Errorf("incident already created for alert: %s", existing.IncidentID)
		}
	}

	// Get PagerDuty config
	config, err := s.repo.GetPagerDutyConfig(ctx)
	if err != nil {
		return nil, err
	}
	if config == nil {
		return nil, fmt.Errorf("pagerduty not configured")
	}

	// Create PagerDuty client
	pdClient := clients.NewPagerDutyClient(config, s.logger)

	// Create incident
	incident, err := pdClient.CreateIncident(title, description, urgency)
	if err != nil {
		return nil, fmt.Errorf("failed to create pagerduty incident: %w", err)
	}

	// Save mapping if alertID provided
	if alertID != "" {
		mapping := &models.AlertPagerDutyMapping{
			AlertID:    alertID,
			IncidentID: incident.ID,
			CreatedAt:  time.Now(),
			UpdatedAt:  time.Now(),
		}
		if err := s.repo.CreateAlertPagerDutyMapping(ctx, mapping); err != nil {
			s.logger.Warn("Failed to save alert-pagerduty mapping", zap.Error(err))
		}
	}

	s.logger.Info("Created PagerDuty incident",
		zap.String("incident_id", incident.ID),
		zap.String("alert_id", alertID))

	return incident, nil
}

func (s *IntegrationService) GetPagerDutyIncident(ctx context.Context, incidentID string) (*models.PagerDutyIncident, error) {
	config, err := s.repo.GetPagerDutyConfig(ctx)
	if err != nil {
		return nil, err
	}
	if config == nil {
		return nil, fmt.Errorf("pagerduty not configured")
	}

	pdClient := clients.NewPagerDutyClient(config, s.logger)
	return pdClient.GetIncident(incidentID)
}

func (s *IntegrationService) UpdatePagerDutyIncident(ctx context.Context, incidentID, status, resolution string) error {
	config, err := s.repo.GetPagerDutyConfig(ctx)
	if err != nil {
		return err
	}
	if config == nil {
		return fmt.Errorf("pagerduty not configured")
	}

	pdClient := clients.NewPagerDutyClient(config, s.logger)
	return pdClient.UpdateIncident(incidentID, status, resolution)
}

func (s *IntegrationService) GetPagerDutyConfig(ctx context.Context) (*models.PagerDutyConfig, error) {
	return s.repo.GetPagerDutyConfig(ctx)
}

func (s *IntegrationService) UpdatePagerDutyConfig(ctx context.Context, config *models.PagerDutyConfig) error {
	config.UpdatedAt = time.Now()
	if config.CreatedAt.IsZero() {
		config.CreatedAt = time.Now()
	}

	// Test connection
	pdClient := clients.NewPagerDutyClient(config, s.logger)
	if err := pdClient.TestConnection(); err != nil {
		return fmt.Errorf("pagerduty connection test failed: %w", err)
	}

	return s.repo.UpsertPagerDutyConfig(ctx, config)
}

// Helper methods
func (s *IntegrationService) mapPriorityFromJira(priority string) string {
	switch priority {
	case "Highest", "Critical":
		return "critical"
	case "High":
		return "high"
	case "Medium":
		return "medium"
	case "Low", "Lowest":
		return "low"
	default:
		return "medium"
	}
}
